package com.egg.gateway.filter;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.egg.auth.model.LoginUserInfo;
import com.egg.common.context.util.LocalThreadContextUtils;
import com.egg.common.core.constant.CoreConstant;
import com.egg.common.util.ConvertUtil;
import com.egg.gateway.config.WhiteConfig;
import com.egg.gateway.constant.FilterOrderConstant;
import com.egg.gateway.service.FeignService;
import lombok.Data;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.core.io.buffer.DataBufferUtils;
import org.springframework.http.MediaType;
import org.springframework.http.codec.HttpMessageReader;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpRequestDecorator;
import org.springframework.stereotype.Component;
import org.springframework.web.reactive.function.server.HandlerStrategies;
import org.springframework.web.reactive.function.server.ServerRequest;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

import java.util.List;
import java.util.concurrent.Future;

/**
 * @author east
 */
@Slf4j
@Component
public class ContextFilter implements GlobalFilter, Ordered {


    @Autowired
    private FeignService feignService;
    @Autowired
    private WhiteConfig whiteConfig;


    @Data
    public static class GatewayContext {
        public static final String CACHE_GATEWAY_CONTEXT = "cacheGatewayContext";
        private Long userId;
        private String parameter;
        private List<String> funcPerms;
        private List<Long> dataPerms;
    }


    @Override
    public int getOrder() {
        return FilterOrderConstant.CONTEXT_FILTER_ORDER;
    }

    private static final List<HttpMessageReader<?>> messageReaders = HandlerStrategies.withDefaults().messageReaders();

    private Mono<Void> readBody(ServerWebExchange exchange, GatewayFilterChain chain, GatewayContext gatewayContext) {
        return DataBufferUtils.join(exchange.getRequest().getBody()).flatMap(dataBuffer -> {
            byte[] bytes = new byte[dataBuffer.readableByteCount()];
            dataBuffer.read(bytes);
            DataBufferUtils.release(dataBuffer);
            Flux<DataBuffer> cachedFlux = Flux.defer(() -> {
                DataBuffer buffer = exchange.getResponse().bufferFactory().wrap(bytes);
                DataBufferUtils.retain(buffer);
                return Mono.just(buffer);
            });
            ServerHttpRequest mutatedRequest = new ServerHttpRequestDecorator(exchange.getRequest()) {
                @Override
                public Flux<DataBuffer> getBody() {
                    return cachedFlux;
                }
            };
            ServerWebExchange mutatedExchange = exchange.mutate().request(mutatedRequest).build();
            return ServerRequest.create(mutatedExchange, messageReaders)
                    .bodyToMono(JSONObject.class)
                    .doOnNext(objectValue -> gatewayContext.setParameter(JSON.toJSONString(objectValue))).then(chain.filter(mutatedExchange));
        });
    }

    @SneakyThrows
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        ServerHttpRequest request = exchange.getRequest();
        if (whiteConfig.excludeAuth(request.getURI().getPath())) {
            // 白名单接口, 无法获取用户信息, 重置请求头
            exchange.getRequest().mutate().header(LocalThreadContextUtils.USER_ID_KEY, CoreConstant.DEFAULT_ID.toString());
            exchange.getRequest().mutate().header(LocalThreadContextUtils.DATA_PERM_KEY, "");
            return chain.filter(exchange);
        }
        Future<LoginUserInfo> futureLoginUser = feignService.getLoginUserByToken(request);
        GatewayContext gatewayContext = new GatewayContext();
        gatewayContext.setParameter(JSON.toJSONString(request.getQueryParams()));
        LoginUserInfo loginUserInfo = futureLoginUser.get();
        gatewayContext.setUserId(loginUserInfo.getUserId());
        gatewayContext.setFuncPerms(loginUserInfo.getFuncPerms());
        gatewayContext.setDataPerms(loginUserInfo.getDataPerms());
        // 用户信息通过请求头向子服务传递
        exchange.getRequest().mutate().header(LocalThreadContextUtils.USER_ID_KEY, gatewayContext.getUserId().toString());
        exchange.getRequest().mutate().header(LocalThreadContextUtils.DATA_PERM_KEY, ConvertUtil.listToStr(gatewayContext.getDataPerms()));
        // 注意，因为webflux的响应式编程 不能再采取原先的编码方式 即应该先将gatewayContext放入exchange中，否则其他地方可能取不到
        exchange.getAttributes().put(GatewayContext.CACHE_GATEWAY_CONTEXT, gatewayContext);
        // 处理参数
        if (request.getHeaders().getContentLength() > 0) {
            MediaType contentType = request.getHeaders().getContentType();
            if (MediaType.APPLICATION_JSON.equals(contentType)
                    || MediaType.APPLICATION_JSON_VALUE.equals(contentType)
                    || MediaType.APPLICATION_JSON_UTF8.equals(contentType)
                    || MediaType.APPLICATION_JSON_UTF8_VALUE.equals(contentType)
            ) {
                return readBody(exchange, chain, gatewayContext);
            }
        }
        return chain.filter(exchange);
    }


}